from fastapi import APIRouter, Query, Request,Response,Depends,Cookie,HTTPException,UploadFile,File,Form
#重定向
from fastapi.responses import RedirectResponse
from redis.asyncio import Redis
from fastapi.responses import JSONResponse
from fastapi.templating import Jinja2Templates
from datetime import datetime,timezone
import json
import asyncio
from typing import List
import os
import sys
import yaml
import bisect
import subprocess
import aiofiles
from pydantic import BaseModel
import multiprocessing
from app.plugins.RsaEncrypt import decrypt_data
from app.plugins.AesEncrypt import AESencrypt
from app.plugins.SHA1 import sha1_encrypt
home_router = APIRouter()
BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
path = os.path.join(BASE_DIR, 'app/templates')
templates = Jinja2Templates(directory=path)


def shell_cmd(command):
        # 构造管道命令（注意：awk的单引号需转义，或用双引号包裹整个命令）
    try:
        # 执行命令：shell=True让shell处理管道，text=True让输出为字符串
        result = subprocess.run(
            command,
            shell=True,
            stdout=subprocess.PIPE,  # 捕获标准输出（状态）
            stderr=subprocess.PIPE,  # 捕获标准错误（错误信息）
            text=True,               # 输出为字符串（而非字节）
            timeout=10               # 超时时间（避免命令挂起）
        )
        if result.returncode == 0:
            status = result.stdout.strip()
            return status
        else:
            # 命令执行失败（如qm出错）
            error_msg = result.stderr.strip()
            return f"命令执行失败：{error_msg}"
    except subprocess.TimeoutExpired:
        return "错误：命令执行超时"
    except Exception as e:
        return f"未知错误：{str(e)}"


@home_router.get("/home")
def get_home(request:Request):
    #username = request.state.user
    #return 'ok'
    return templates.TemplateResponse("home.html", {"request": request})

@home_router.get("/home/project")#获取用户项目
async def get_User_project(request:Request):#用户名在token里面获取到
    username = request.state.user
    #username = 'admin'
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        projects = await redis.smembers('projects')
        projects = [json.loads(item) for item in list(projects)]
        if username!="admin":
            user_projects = [project for project in projects if project['author'] == username]
        else:
            user_projects = projects
        for project in user_projects:
            project['status'] = "completed"
    return JSONResponse({"msg": "success","projects":user_projects},status_code=200)



@home_router.post("/home/getProjects")#校验项目名称是否被使用
async def get_projects(request:Request,data:dict):
    projectName = data['projectName']
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        projects = await redis.smembers('projects')
        projects = [json.loads(item) for item in list(projects)]
        for i in projects:
            if i['name'] == projectName:
                return JSONResponse({"msg": "projectName already exists"},status_code=400)
    return JSONResponse({"msg": "success"},status_code=200)

@home_router.post("/home/createProject")
async def create_project(request:Request,data:dict):
    username = request.state.user
    #username = 'admin'
    projectName = data['projectName']
    timenow = datetime.now()
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        projects_id_list = await redis.zrange('projects_id',0,-1)
        if projects_id_list:
            projects_id_list = [int(item) for item in list(projects_id_list)]
            l = projects_id_list[0]
            await redis.zrem('projects_id',str(l))
        else:
            l=1
            for i in range(2,1001):
                await redis.zadd('projects_id',{str(i):i})
        project = {
        "pid":l,
        "name": projectName,
        "datetime": timenow.strftime("%Y-%m-%d %H:%M:%S"),
        "author": username
        }
        await redis.sadd('projects',json.dumps(project))
        for i in range(l*1000+1,l*1000+1001):#创建vmid表
            await redis.zadd(f'{projectName}_vmid',{str(i):i})
            await redis.zadd(f'{projectName}_bridgeid',{str(i):i})
        await redis.zadd(f'{projectName}_vmid',{'na':999999})
        await redis.zadd(f'{projectName}_bridgeid',{'na':999999})
        await redis.hset(f'{projectName}_topo_alledges','alledges','[]')
        await redis.hset(f'{projectName}_topo_allnodes','allnodes','[]')
    # data = await request.json()
    # async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
    #     await redis.sadd('projects',json.dumps(data))
    return JSONResponse({"msg": "success","project":project},status_code=200)



def mul_delete_project(data):
    asyncio.run(delete_project_task(data))

async def delete_project_task(data):
    projectName = data['projectName']
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        #拿到项目里面的所有vmid，删除对应的设备后删除项目
        vmidlist = await redis.smembers(f'{projectName}_map')
        vmidlist = [json.loads(item) for item in list(vmidlist)]
        #取出key=vmid的元素,删除对应的vmid
        vmidlist = [item['vmid'] for item in vmidlist]
        for i in vmidlist:
            #直接删除.conf文件和image文件
            cmd = f'rm -rf /etc/pve/qemu-server/{i}.conf'
            shell_cmd(cmd)
            cmd = f'rm -rf /data/sda/images/{i}'
            shell_cmd(cmd)
        #取出bridge的key，删除网桥
        bridgeidlist = await redis.smembers(f'{projectName}_bridgeid_map')
        bridgeidlist = [json.loads(item) for item in list(bridgeidlist)]
        #取出key=bridgeid的元素,删除对应的bridgeid
        bridgeidlist = [item['bridgeid'] for item in bridgeidlist]
        bridgeidlist = [i.split('vmbr')[1] for i in bridgeidlist]
        for i in bridgeidlist:
            #删除网桥
            # # 关闭网桥（设置为DOWN状态）
            del_bridge_step1 = f'ip link set vmbr{i} down'
            shell_cmd(del_bridge_step1)
            # # 删除网桥（vmbr10）
            del_bridge_step2 = f'brctl delbr vmbr{i}'   
            shell_cmd(del_bridge_step2)
            #删除配置sed -i '/^auto vmbr1$/,/bridge-fd 0$/d' interfaces
            del_bridge_step3 = f"sed -i '/^auto vmbr{i}$/,/bridge-fd 0$/d' /etc/network/interfaces"
            shell_cmd(del_bridge_step3)
        await redis.delete(f'{projectName}_vmid')
        await redis.delete(f'{projectName}_bridgeid')
        await redis.delete(f'{projectName}_topo_alledges')
        await redis.delete(f'{projectName}_topo_allnodes')
        await redis.delete(f'{projectName}_bridgeid_map')
        await redis.delete(f'{projectName}_map')

        #删除projects里面名字一样的项目
        projects = await redis.smembers('projects')
        projects = [json.loads(item) for item in list(projects)]
        for i in projects:
            if i['name'] == projectName:
                await redis.srem('projects',json.dumps(i))
                await redis.zadd('projects_id',{str(i['pid']):int(i['pid'])})
                break

@home_router.post("/home/deleteProject")
async def delete_project(request:Request,data:dict):
    multiprocessing.Process(target=mul_delete_project, args=(data,)).start()
    return JSONResponse({"msg": "success"},status_code=200)


#判断哪个vmid可以使用的方法
def find_available_vmid_binary(used_vmids, start_vmid=101):
    # 确保数组已排序
    sorted_used = sorted(used_vmids)
    # 使用二分查找找到start_vmid的插入位置
    pos = bisect.bisect_left(sorted_used, start_vmid)
    # 如果start_vmid不在已使用列表中，直接返回
    if pos >= len(sorted_used) or sorted_used[pos] != start_vmid:
        return start_vmid
    # 查找第一个空缺
    for i in range(pos, len(sorted_used) - 1):
        if sorted_used[i + 1] - sorted_used[i] > 1:
            return sorted_used[i] + 1
    # 如果所有vmid都被使用，返回最大的vmid + 1
    return sorted_used[-1] + 1

async def add_template_task(payload_str,files):
    payload = json.loads(payload_str)
    templateName = payload['DeviceVersion']
    ymlname = templateName.split('-')[0]
    #CPU、内存从设备配置里面读取出来
    # CPUCORE = payload['CPUCORE']
    # MEMORY = payload['MEMORY']
    DiskSize = payload['DiskSize']
    SystemType = payload['SystemType']
    if SystemType == 'uefi':
        SystemType='ovmf'
    else:
        SystemType='seabios'
    InterfaceTypes = payload['InterfaceTypes']
    imagefile = files[0]
    # #读取yml文件
    BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
    path_yml = os.path.join(BASE_DIR, f'app/config/yml/{ymlname}.yml')
    with open(path_yml, 'r', encoding='utf-8') as f:
        ymldata =  yaml.safe_load(f.read())
    # 保存qcow2文件
    path_qcow2_dir = os.path.join(BASE_DIR, f'app/qcow2/qemu/{templateName}')
    if not os.path.exists(path_qcow2_dir):
        os.makedirs(path_qcow2_dir)
    path_qcow2 = os.path.join(BASE_DIR, f'app/qcow2/qemu/{templateName}/hda.qcow2')
    # with open(path_qcow2, 'wb') as f:
    #     f.write(imagefile.file.read())
    # 分块写入文件
    total_size = 0
    async with aiofiles.open(path_qcow2, 'wb') as f:
        while chunk := await imagefile.read(1024 * 1024):  # 1MB 分块
            await f.write(chunk)
            total_size += len(chunk)
    #写完文件后，导入设备
    #导入qcow2到pve里面
    # vmid通过读取redis得到，从device_template里面获取,判断是否存在，存在就删除，不存在就创建
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        device_template = await redis.smembers('device_template')
        device_template = [json.loads(item) for item in list(device_template)]
        #判断是否存在    name=templateName
        #判断哪个vmid可以使用
        vmidlist = [int(i['vmid']) for i in device_template]
        vmidlist = sorted(vmidlist)
        vmid = 101
        vmid = find_available_vmid_binary(vmidlist, vmid)
        for i in device_template: 
            if i['name'] == templateName:
                vmid = i['vmid']
                #获取到vmid后，删除对应的设备
                cmd = f'chattr -i /data/sda/images/{vmid}/base-{vmid}-disk-0.qcow2'
                shell_cmd(cmd)
                #直接删除.conf文件和image文件
                cmd = f'rm -rf /etc/pve/qemu-server/{vmid}.conf'
                shell_cmd(cmd)
                cmd = f'rm -rf /data/sda/images/{vmid}'
                shell_cmd(cmd)
                break
        obj = {
            "name":templateName,
            "vmid":vmid,
        }
        await redis.sadd('device_template',json.dumps(obj))#添加映射关系
        #获取接口数量
        ethernet = ymldata.get("ethernet")
        eth_name = ymldata.get('eth_name')
        # cpu: 4
        # ram: 4096
        CPUCORE = ymldata.get("cpu")
        MEMORY = ymldata.get("ram")
        if eth_name:
            eth_number = len(eth_name)+ethernet
        else:
            eth_number=ethernet
        eth_str = ''
        for i in range(eth_number):
            k = f'--net{i} {InterfaceTypes},bridge=tmpbr0,link_down=1 '
            eth_str = eth_str +k
        systemCPU = subprocess.run('nproc', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
        cmd = f'qm create {vmid} --name {templateName} --memory {MEMORY} --cores {CPUCORE} --cpu host --affinity 1-{int(systemCPU.stdout)-1} --cpuunits 95 --bios {SystemType}  {eth_str} --sata0 local-sda:{DiskSize},format=qcow2'
        shell_cmd(cmd)
        cmd = f'qm importdisk {vmid} {path_qcow2} local-sda --format=qcow2 '
        shell_cmd(cmd)
        #修改引导顺序
        cmd = f'rm -rf /data/sda/images/{vmid}/vm-{vmid}-disk-0.qcow2 '
        shell_cmd(cmd)
        cmd = f'mv /data/sda/images/{vmid}/vm-{vmid}-disk-1.qcow2 /data/sda/images/{vmid}/vm-{vmid}-disk-0.qcow2'
        shell_cmd(cmd)
        #导入进来后转成模板
        cmd = f'qm template {vmid}'
        shell_cmd(cmd)
    



@home_router.post("/home/addTemplate")
async def add_template(request:Request,payload_str:str= Form(...),files:list[UploadFile]=File(...)):
    await add_template_task(payload_str,files)
    return JSONResponse({"msg": "success"},status_code=200)

async def add_yml_task(payload_str,files):
    payload = json.loads(payload_str)
    ymlName = payload['ymlName']
    ymlfile = files[0]
    # 保存yml文件
    BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
    path_yml = os.path.join(BASE_DIR, f'app/config/yml/{ymlName}.yml')
    with open(path_yml, 'wb') as f:
        f.write(ymlfile.file.read())
@home_router.post("/home/addYml")
async def add_yml(request:Request,payload_str:str= Form(...),files:list[UploadFile]=File(...)):
    await add_yml_task(payload_str,files)
    return JSONResponse({"msg": "success"},status_code=200)

@home_router.get("/home/templateList")
async def get_templateList():
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        device_template = await redis.smembers('device_template')
        device_template = [json.loads(item) for item in list(device_template)]
        return JSONResponse({"msg": "success","data":device_template},status_code=200)

@home_router.post("/home/deleteTemplate")
async def delete_template(request:Request,data:dict):
    vmid = data["vmid"]
    BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        device_template = await redis.smembers('device_template')
        device_template = [json.loads(item) for item in list(device_template)]
        for i in device_template:
            if i['vmid'] == vmid:
                name = i['name']
                path_template = os.path.join(BASE_DIR, f'app/qcow2/qemu/{name}')
                cmd = f'rm -rf {path_template}'
                shell_cmd(cmd)
                await redis.srem('device_template',json.dumps(i))
                #直接删除.conf文件和image文件
                cmd = f'chattr -i /data/sda/images/{vmid}/base-{vmid}-disk-0.qcow2'
                shell_cmd(cmd)
                cmd = f'rm -rf /etc/pve/qemu-server/{vmid}.conf'
                shell_cmd(cmd)
                cmd = f'rm -rf /data/sda/images/{vmid}'
                shell_cmd(cmd)

                break
    return JSONResponse({"msg": "success"},status_code=200)


@home_router.post("/home/checkPwd")
async def check_pwd(request: Request,data:dict):
    #username='admin'
    username = request.state.user
    password = data['password']
    password = decrypt_data(password)
    password = sha1_encrypt(password)
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        userinfo = await redis.smembers('userinfo')
        userinfo = [json.loads(item) for item in list(userinfo)]
        for item in userinfo:
            if item['name'] == username and item['password'] == password:
                return JSONResponse({"msg": "success"},status_code=200)
    return JSONResponse({"msg": "error"},status_code=400)




@home_router.post("/home/changepassword")
async def change_password(request: Request,data:dict):
    # username='admin'
    username = request.state.user
    password = data['password']
    password = decrypt_data(password)
    password = sha1_encrypt(password)
    oldpassword = data['oldpassword']
    oldpassword = decrypt_data(oldpassword)
    oldpassword = sha1_encrypt(oldpassword)
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        userinfo = await redis.smembers('userinfo')
        userinfo = [json.loads(item) for item in list(userinfo)]
        for item in userinfo:
            if item['name'] == username and item['password'] == oldpassword:
                await redis.srem('userinfo',json.dumps({"name":username,"password":oldpassword}))
                await redis.sadd('userinfo',json.dumps({"name":username,"password":password}))
                break
    return JSONResponse(content={"message": "success"},status_code=200)


@home_router.get("/home/getCustomTemplate")
async def get_CustomTemplate():
    #获取设备列表，添加到redis，添加的时候需要判断是否已经存在，存在就不添加
    result = subprocess.run(
                'qm list',              # 要执行的命令
                shell=True,               # 使用shell执行命令
                stdout=subprocess.PIPE,   # 获取命令输出
                stderr=subprocess.PIPE,   # 获取命令错误输出
                text=True,               # 输出为字符串（而非字节）
                timeout=10               # 超时时间（避免命令挂起）
    )
    data_str = result.stdout.strip()
    lines = [line.strip() for line in data_str.split('\n') if line.strip()]
    result = []#[{ 'name': '128T-1','vmid': 101}, {'name': 'cs0-test-cpe00','vmid': 20000, }]
    for line in lines[1:]:  # 从第二行开始处理
        parts = [part for part in line.split() if part]
        vmid = int(parts[0])
        name = parts[1]
        if vmid<=1000:
            result.append({
                "name": name,
                "vmid": vmid,
            })
    
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        for i in result:
            await redis.sadd('device_template',json.dumps(i))
            BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
            path_template = os.path.join(BASE_DIR, f'app/qcow2/qemu/{i["name"]}')
            if not os.path.exists(path_template):
                os.makedirs(path_template)
    return JSONResponse({"msg": "success"},status_code=200)